package cn.edu.scau.cmi.ema.service;

import java.util.List;
import java.util.Set;

import org.springframework.stereotype.Service;

import org.springframework.transaction.annotation.Transactional;


import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Projections;
import org.hibernate.Session;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Projections;
import org.hibernate.Session;
import cn.edu.scau.cmi.ema.dao.*;
import cn.edu.scau.cmi.ema.domain.*;
import cn.edu.scau.cmi.ema.util.CmiSqlUtil;
import cn.edu.scau.cmi.ema.domain.base.CmiPagination;
import cn.edu.scau.cmi.ema.service.base.CmiEmaService;

import java.io.IOException;
import java.util.Map;
import java.util.HashMap;
import java.util.Set;
import java.util.HashSet;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

import javax.persistence.Query;
import java.util.Iterator;
@Service("HazardService")
@Transactional

public class HazardServiceImpl extends CmiEmaService implements HazardService {
	public HazardServiceImpl() {
	}

	@Transactional
	public Hazard saveHazard(Hazard hazard) {
		Set<Eventhazard> existingEventhazardsForHazard = new HashSet<Eventhazard>();
		Set<Fillerhazard> existingFillerhazardsForHazard = new HashSet<Fillerhazard>();
		Set<Fillertype> existingFillertypesForFillertype = new HashSet<Fillertype>();

		Set<Eventhazard> selectedEventhazardsForHazard = hazard.getEventhazardsForHazard();
		Set<Fillerhazard> selectedFillerhazardsForHazard = hazard.getFillerhazardsForHazard();
		Set<Fillertype> selectedFillertypesForFillertype = hazard.getFillertypesForHazard();

		if (hazard.getId() == null) {

			Hazard existingHazard = hazardDAO.findHazardByPhysicalPrimaryKey(hazard.getName());

			if (isEquals(hazard, existingHazard)) {
				return existingHazard;
			}

			hazard = hazardDAO.store(hazard);
			hazardDAO.flush();

			for (Eventhazard relatedEventhazard : selectedEventhazardsForHazard) {
				relatedEventhazard.setHazard(hazard);
				eventhazardDAO.store(relatedEventhazard);
			}
			for (Fillerhazard relatedFillerhazard : selectedFillerhazardsForHazard) {
				relatedFillerhazard.setHazard(hazard);
				fillerhazardDAO.store(relatedFillerhazard);
			}

			for (Fillertype relatedFillertype : selectedFillertypesForFillertype) {
				relatedFillertype.getHazardsForFillertype().add(hazard);
				fillertypeDAO.store(relatedFillertype);
			}
		} else {
			Hazard existingHazard = hazardDAO.findHazardByPrimaryKey(hazard.getId());

			existingEventhazardsForHazard = existingHazard.getEventhazardsForHazard();
			existingFillerhazardsForHazard = existingHazard.getFillerhazardsForHazard();
			existingFillertypesForFillertype = existingHazard.getFillertypesForHazard();

			Set<Eventhazard> prepareDeleteEventhazardsForHazard = new HashSet<Eventhazard>();
			Set<Eventhazard> prepareAddEventhazardsForHazard = new HashSet<Eventhazard>();

			prepareDeleteEventhazardsForHazard.addAll(selectedEventhazardsForHazard);
			prepareDeleteEventhazardsForHazard.addAll(existingEventhazardsForHazard);

			prepareAddEventhazardsForHazard.addAll(selectedEventhazardsForHazard);
			prepareAddEventhazardsForHazard.addAll(existingEventhazardsForHazard);

			prepareDeleteEventhazardsForHazard.removeAll(selectedEventhazardsForHazard);
			prepareAddEventhazardsForHazard.removeAll(existingEventhazardsForHazard);

			for (Eventhazard relatedEventhazard : prepareAddEventhazardsForHazard) {
				relatedEventhazard.setHazard(hazard);
				eventhazardDAO.store(relatedEventhazard);
			}

			for (Eventhazard relatedEventhazard : prepareDeleteEventhazardsForHazard) {
				relatedEventhazard.setHazard(null);
				eventhazardDAO.store(relatedEventhazard);
			}
			Set<Fillerhazard> prepareDeleteFillerhazardsForHazard = new HashSet<Fillerhazard>();
			Set<Fillerhazard> prepareAddFillerhazardsForHazard = new HashSet<Fillerhazard>();

			prepareDeleteFillerhazardsForHazard.addAll(selectedFillerhazardsForHazard);
			prepareDeleteFillerhazardsForHazard.addAll(existingFillerhazardsForHazard);

			prepareAddFillerhazardsForHazard.addAll(selectedFillerhazardsForHazard);
			prepareAddFillerhazardsForHazard.addAll(existingFillerhazardsForHazard);

			prepareDeleteFillerhazardsForHazard.removeAll(selectedFillerhazardsForHazard);
			prepareAddFillerhazardsForHazard.removeAll(existingFillerhazardsForHazard);

			for (Fillerhazard relatedFillerhazard : prepareAddFillerhazardsForHazard) {
				relatedFillerhazard.setHazard(hazard);
				fillerhazardDAO.store(relatedFillerhazard);
			}

			for (Fillerhazard relatedFillerhazard : prepareDeleteFillerhazardsForHazard) {
				relatedFillerhazard.setHazard(null);
				fillerhazardDAO.store(relatedFillerhazard);
			}
			Set<Fillertype> prepareDeleteFillertypesForFillertype = new HashSet<Fillertype>();
			Set<Fillertype> prepareAddFillertypesForFillertype = new HashSet<Fillertype>();

			prepareDeleteFillertypesForFillertype.addAll(selectedFillertypesForFillertype);
			prepareDeleteFillertypesForFillertype.addAll(existingFillertypesForFillertype);

			prepareAddFillertypesForFillertype.addAll(selectedFillertypesForFillertype);
			prepareAddFillertypesForFillertype.addAll(existingFillertypesForFillertype);

			prepareDeleteFillertypesForFillertype.removeAll(selectedFillertypesForFillertype);
			prepareAddFillertypesForFillertype.removeAll(existingFillertypesForFillertype);

			for (Fillertype relatedFillertype : prepareAddFillertypesForFillertype) {

				relatedFillertype.getHazardsForFillertype().add(existingHazard);
				fillertypeDAO.store(relatedFillertype);
			}

			for (Fillertype relatedFillertype : prepareDeleteFillertypesForFillertype) {

				relatedFillertype.getHazardsForFillertype().remove(existingHazard);
				fillertypeDAO.store(relatedFillertype);
			}
			hazard = hazardDAO.store(hazard);
			hazardDAO.flush();
		}

		eventhazardDAO.flush();
		fillerhazardDAO.flush();
		fillertypeDAO.flush();
		return hazard;
	}

	public void deleteHazard(Hazard hazard) {
		hazardDAO.remove(hazard);
		hazardDAO.flush();
	}

	@Transactional
	public void deleteHazards(Set<Integer> deleteHazardsId) {
		Query query = hazardDAO.createNamedQuery("deleteHazardsById", -1, -1);
		query.setParameter(1, deleteHazardsId);
		query.executeUpdate();
	}

	@Transactional
	public Set<Hazard> loadHazards() {
		return hazardDAO.findAllHazards();

	}

	@Transactional
	public Set<Hazard> loadReferenceHazards(Set<Hazard> hazards) {
		Iterator<Hazard> hazardsIterator = hazards.iterator();
		Set<Hazard> referencedHazards = new HashSet<>();
		while (hazardsIterator.hasNext()) {
			Hazard currentHazard = hazardsIterator.next();

			if (!currentHazard.getEventhazardsForHazard().isEmpty()) {
				referencedHazards.add(currentHazard);
				continue;
			}

			if (!currentHazard.getFillerhazardsForHazard().isEmpty()) {
				referencedHazards.add(currentHazard);
				continue;
			}

			if (!currentHazard.getFillertypesForHazard().isEmpty()) {
				referencedHazards.add(currentHazard);
				continue;
			}
		}
		return referencedHazards;
	}

	public Set<Hazard> loadHazardsByIdSet(Set<Integer> hazardIdSet) {

		Iterator<Integer> hazardIdIterator = hazardIdSet.iterator();
		Set<Hazard> hazardSet = new HashSet<>();
		while (hazardIdIterator.hasNext()) {
			Integer id = hazardIdIterator.next();
			Hazard currentHazard = hazardDAO.findHazardById(id);
			hazardSet.add(currentHazard);
		}
		return hazardSet;
	}

	@Transactional
	public List<Hazard> findAllHazards(Integer startResult, Integer maxRows) {
		return new java.util.ArrayList<Hazard>(hazardDAO.findAllHazards(startResult, maxRows));
	}

	@Transactional
	public Hazard findHazardByPrimaryKey(Integer id) {
		return hazardDAO.findHazardByPrimaryKey(id);
	}

	@Transactional
	public Integer countHazards() {
		return ((Long) hazardDAO.createQuerySingleResult("select count(o) from Hazard o").getSingleResult()).intValue();
	}

	@Transactional
	public Integer countHazards(CmiPagination page) {
		String sql = CmiSqlUtil.createCountHql(page, Hazard.class);
		return ((Long) hazardDAO.createQuerySingleResult(sql).getSingleResult()).intValue();
	}

	/**
	 * search for name with ajax
	 * 
	 */
	@Transactional
	public Integer countHazardsByName(String queryName) {
		Query query = hazardDAO.createNamedQuery("countHazardsByName", -1, -1);
		query.setParameter(1, queryName);
		return new Long((long) query.getSingleResult()).intValue();
	}

	@Transactional
	public Integer countHazardsByDetachedCriteria(CmiPagination page) {
		DetachedCriteria detachedCriteria = CmiSqlUtil.createDetachedCriteria(page, Hazard.class);
		Session session = entityManager.unwrap(Session.class);
		detachedCriteria.setProjection(Projections.rowCount());
		return new Long((long) detachedCriteria.getExecutableCriteria(session).uniqueResult()).intValue();
	}

	public Map<String, ?> loadHazardObjectReferencedSet(Hazard hazard) {
		HashMap<String, Object> objectReferencedSetMap = new HashMap<>();

		Set<Integer> eventhazardsIds = new HashSet<Integer>();
		for (Eventhazard obj : hazard.getEventhazardsForHazard()) {

			eventhazardsIds.add(obj.getId());
		}
		objectReferencedSetMap.put("eventhazardsIds", eventhazardsIds);
		Set<Integer> fillerhazardsIds = new HashSet<Integer>();
		for (Fillerhazard obj : hazard.getFillerhazardsForHazard()) {

			fillerhazardsIds.add(obj.getId());
		}
		objectReferencedSetMap.put("fillerhazardsIds", fillerhazardsIds);
		Set<Integer> fillertypesIds = new HashSet<Integer>();
		for (Fillertype fillertype : hazard.getFillertypesForHazard()) {
			fillertypesIds.add(fillertype.getId());
		}
		objectReferencedSetMap.put("fillertypesIds", fillertypesIds);

		return objectReferencedSetMap;
	}

	public Map<String, ?> loadHazardClassReferencedSet(Hazard hazard) {
		HashMap<String, Object> classReferencedSetMap = new HashMap<>();
		classReferencedSetMap.put("eventhazards", eventhazardDAO.findAllEventhazards());
		classReferencedSetMap.put("fillerhazards", fillerhazardDAO.findAllFillerhazards());
		classReferencedSetMap.put("fillertypes", fillertypeDAO.findAllFillertypes());

		return classReferencedSetMap;
	}

	public boolean isEquals(Hazard hazard, Object obj) {
		if (obj == hazard)
			return true;
		if (!(obj instanceof Hazard))
			return false;
		Hazard comparedHazard = (Hazard) obj;
		if (!CheckPropertyEqual(hazard.getName(), comparedHazard.getName())) {
			return false;
		}
		return true;
	}

	public static <T> boolean CheckPropertyEqual(T originProperty, T comparedProperty) {
		// 基本类型经过参数传递后会自动装箱
		String[] className = originProperty.getClass().getName().split("\\.");
		if (className[0].equals("java") && className[1].equals("math")) {
			return ((Comparable) originProperty).compareTo(comparedProperty) == 0 ? true : false;
		} else {
			return originProperty.equals(comparedProperty);
		}
	}
}

